﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Cysharp.Threading.Tasks;
using Internal.KuiHuaBaoDian.Services.UGUI;
using Internal.KuiHuaBaoDian.Services.UI;
using KuiHuaBaoDian.Services.Asset;
using KuiHuaBaoDian.Services.UGUI;
using UnityEngine;
using UnityEngine.Assertions;

namespace KuiHuaBaoDian.Services.UGUI {

    public interface IUI<TView, THandle> : IUI<TView> where TView : IUIView where THandle : UIHandle {

        new THandle Handle { get; }
    }

    public interface IUI<TView> : IUI where TView : IUIView {

        TView View { get; }
    }

    public interface IExtendableUI {
    }

    public partial interface IUI : IEntity {

        string SortingLayerName { get; }

        string AssetKey { get; }

        string Name { get; }

        string InstanceName { get; }

        string Layer { get; }

        int CameraIndex { get; }

        int SortingLayer { get; }

        short SortingOrder { get; }

        UIHandle Handle { get; }

        bool IsActiveSelf { get; }

        UniTask Preload(IAssetDownloadProgress progress);

        IUIEventListener AsEventListener();
    }

    public abstract class KHBD_UI<TView, THandle> : KHBD_UI<TView>, IUI<TView, THandle> where TView : IUIView where THandle : UIHandle {

        public new THandle Handle => (THandle)base.Handle;

        public KHBD_UI() : base() {
            base.Handle = Activator.CreateInstance<THandle>();
        }
    }

    public abstract class KHBD_UI<TView> : KHBD_Entity, IUI<TView>, IUIInternal, IUIEventListener, IUIExtend where TView : IUIView {

        public IUIEventListener AsEventListener() => this;

        private record ListenerInfoRecord(Type Type, MethodInfo MethodInfo);

        private static readonly IDictionary<Type, HashSet<ListenerInfoRecord>> LISTENER_DICTIONARY = new Dictionary<Type, HashSet<ListenerInfoRecord>>();

        public abstract string Name { get; }

        public abstract string Layer { get; }

        public virtual int CameraIndex { get; } = -1;

        public abstract string AssetKey { get; }

        public abstract UniTask Preload(IAssetDownloadProgress progress);

        GameObject IUIInternal.GameObject => GameObject;
        protected GameObject GameObject { get; private set; }

        public string InstanceName => GameObject.name;

        protected CanvasGroup CanvasGroup { get; private set; }

        public short SortingOrder => (short)Canvas.sortingOrder;

        public int SortingLayer => Canvas.sortingLayerID;

        protected uint SortingOrderReserveExtraSize { get; set; }

        public string SortingLayerName => Canvas.sortingLayerName;

        public UIHandle Handle { get; protected set; } = new UIHandle();

        public TView View { get; private set; }

        protected Canvas Canvas { get; private set; }

        public bool IsActiveSelf => GameObject.activeSelf;

        private IUIEventDispatcherInternal m_EventDispatcher;

        private static readonly Dictionary<Type, List<ConstructorInfo>> m_ConstructorDictionary = new();

        private record ListenerRecord(Type Type, Func<IUIEvent, UniTask> Listener);
        private HashSet<ListenerRecord> m_ListenerRecords = new();

        private List<IUIExtend> m_ExtendList = new();

        public KHBD_UI() : base() {
            var type = GetType();
            if (!m_ConstructorDictionary.TryGetValue(type, out var constructorInfoList)) {
                constructorInfoList = m_ConstructorDictionary[type] = new List<ConstructorInfo>();
                var attributes = type.GetCustomAttributes<UIExtendAttribute>();
                var extendType = typeof(IUIExtend);
                if (attributes != null) {
                    var itor = from attribute in attributes where !attribute.Type.IsInterface where !attribute.Type.IsAbstract where !attribute.Type.IsGenericTypeDefinition where extendType.IsAssignableFrom(attribute.Type) select attribute.Type;
                    foreach (var item in itor) {
                        var constructorInfos = item.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
                        var constructorInfo = constructorInfos.Where(i => i.GetParameters().Length == 1 && typeof(IExtendableUI).IsAssignableFrom(i.GetParameters()[0].ParameterType)).FirstOrDefault();
                        if (constructorInfo != null) {
                            constructorInfoList.Add(constructorInfo);
                        }
                    }
                }
            }
            var args = new object[] { this };
            foreach (var constructorInfo in constructorInfoList) {
                var extend = (IUIExtend)constructorInfo.Invoke(args);
                m_ExtendList.Add(extend);
            }
        }

        protected virtual void OnInitialized() { }

        void IUIInternal.Initialize(Canvas canvas, IUIEventDispatcher eventDispatcher) {
            Assert.IsNotNull(canvas);
            Assert.IsNotNull(eventDispatcher);
            Assert.IsTrue(eventDispatcher is IUIEventDispatcherInternal);

            m_EventDispatcher = eventDispatcher as IUIEventDispatcherInternal;
            AddListeners();
            GameObject = canvas.gameObject;
            CanvasGroup = canvas.GetComponent<CanvasGroup>();
            Canvas = canvas;
            View = canvas.GetComponent<TView>();
            Assert.IsTrue(View != null, $"{nameof(TView)} 未找到：{typeof(TView).FullName}");
            (View as IUIViewInternal)?.SetUI(this);
            OnInitialized();
            Canvas.enabled = false;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        protected virtual UniTask OnInstantiated() => UniTask.CompletedTask;
        UniTask IUIInternal.Instantiated() => OnInstantiated();

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        void IUIExtend.OnViewStart() => OnViewStart();
        private void OnViewStart() {
            Canvas.enabled = true;
            if (m_ExtendList.Count > 0) {
                foreach (var extend in m_ExtendList) {
                    extend.OnViewStart();
                }
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        void IUIExtend.OnViewEnable() => OnViewEnable();
        private void OnViewEnable() {
            if (m_ExtendList.Count > 0) {
                foreach (var extend in m_ExtendList) {
                    extend.OnViewEnable();
                }
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        void IUIExtend.OnViewDisable() => OnViewDisable();
        private void OnViewDisable() {
            if (m_ExtendList.Count > 0) {
                foreach (var extend in m_ExtendList) {
                    extend.OnViewDisable();
                }
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        void IUIExtend.OnViewDestroy() => OnViewDestroy();
        private void OnViewDestroy() {
            if (m_ExtendList.Count > 0) {
                foreach (var extend in m_ExtendList) {
                    extend.OnViewDestroy();
                }
                m_ExtendList = null; // 一般会发生代码先调用了这个类的Dispose，所以设null需要在View的OnDestroy之中
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        void IUIExtend.OnViewUpdate() => OnViewUpdate();
        private void OnViewUpdate() {
            if (m_ExtendList.Count > 0) {
                foreach (var extend in m_ExtendList) {
                    extend.OnViewUpdate();
                }
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        protected virtual UniTask OnBackIn() => UniTask.CompletedTask;
        UniTask IUIInternal.OnBackIn() => OnBackIn();

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        protected virtual UniTask OnGotoOut() => UniTask.CompletedTask;
        UniTask IUIInternal.OnGotoOut() => OnGotoOut();

        private void AddListeners() {
            var type = GetType();
            if (LISTENER_DICTIONARY.TryGetValue(type, out var listenerInfoSet)) {
                foreach (var listenerInfo in listenerInfoSet) {
                    AddListener(listenerInfo.Type, listenerInfo.MethodInfo);
                }
            } else {
                listenerInfoSet = new HashSet<ListenerInfoRecord>();
                LISTENER_DICTIONARY.Add(type, listenerInfoSet);
                var methodInfos = GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
                foreach (var methodInfo in methodInfos) {
                    var listenAttribute = methodInfo.GetCustomAttribute<UIEventListenerAttribute>();
                    if (listenAttribute != null) {
                        var parameters = methodInfo.GetParameters();
                        Assert.IsNotNull(parameters, $"[{nameof(UIEventListenerAttribute)[..^9]}]监听函数{methodInfo.DeclaringType}.{methodInfo.Name}必须有且只有一个实现{nameof(IUIEvent)}接口的参数");
                        Assert.IsTrue(parameters.Length == 1, $"[{nameof(UIEventListenerAttribute)[..^9]}]监听函数{methodInfo.DeclaringType}.{methodInfo.Name}必须有且只有一个实现{nameof(IUIEvent)}接口的参数, 当前参数个数为{parameters.Length}");
                        var parameterType = parameters[0].ParameterType;
                        Assert.IsTrue(typeof(IUIEvent).IsAssignableFrom(parameterType), $"[{nameof(UIEventListenerAttribute)[..^9]}]监听函数{methodInfo.DeclaringType}.{methodInfo.Name}必须有且只有一个实现{nameof(IUIEvent)}接口的参数, 当前参数类型为{parameterType.Name}，未实现{nameof(IUIEvent)}");
                        var returnType = methodInfo.ReturnType;
                        Assert.IsTrue(typeof(UniTask) == returnType, $"[{nameof(UIEventListenerAttribute)[..^9]}]监听函数{methodInfo.DeclaringType}.{methodInfo.Name}的返回类型必须是{nameof(UniTask)}, 当前返回类型为{methodInfo.ReflectedType}");
                        AddListener(parameterType, methodInfo);
                        listenerInfoSet.Add(new ListenerInfoRecord(parameterType, methodInfo));
                    }
                }
            }
        }

        private void AddListener(Type eventType, MethodInfo methodInfo) {
            UniTask listener(IUIEvent @event) {
                return (UniTask)methodInfo.Invoke(this, new object[] { @event });
            }
            m_ListenerRecords.Add(new ListenerRecord(eventType, listener));
            AddListener(eventType, listener);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void AddListener(Type eventType, Func<IUIEvent, UniTask> func) => m_EventDispatcher.AsListener().AddListener(@eventType, func);
        void IUIEventListener.AddListener(Type eventType, Func<IUIEvent, UniTask> func) => AddListener(eventType, func);

        private void RemoveListeners() {
            foreach (var listenerRecord in m_ListenerRecords) {
                RemoveListener(listenerRecord.Type, listenerRecord.Listener);
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void RemoveListener(Type eventType, Func<IUIEvent, UniTask> func) => m_EventDispatcher.AsListener().RemoveListener(@eventType, func);
        void IUIEventListener.RemoveListener(Type eventType, Func<IUIEvent, UniTask> func) => RemoveListener(eventType, func);

        protected UniTask DispatchEvent(IUIEvent @event) => m_EventDispatcher.Dispatch(@event);

        protected override void Dispose() {
            if (m_ExtendList.Count > 0) {
                foreach (var extend in m_ExtendList) {
                    extend.Dispose();
                }
            }
            if (GameObject != null) {
                AssetService.Instance.Release(GameObject);
            }
            GameObject = null;
            CanvasGroup = null;
            View = default;
            Canvas = null;
            Handle = null;

            RemoveListeners();
            m_ListenerRecords = null;
            m_EventDispatcher = null;
            base.Dispose();
        }

        int IUIInternal.SetSortingOrder(int value) {
            value -= (int)SortingOrderReserveExtraSize;
            Canvas.sortingOrder = value;
            (View as IUIViewInternal)?.FixSorting();
            return value;
        }

        void IUIInternal.SetSortingLayer(int value) {
            Canvas.sortingLayerID = value;
            (View as IUIViewInternal)?.FixSorting();
        }

        void IUIInternal.SetSortingLayerName(string value) {
            Canvas.sortingLayerName = value;
            (View as IUIViewInternal)?.FixSorting();
        }

        void IUIInternal.SetActive(bool value) {
            Canvas.gameObject.SetActive(value);
        }

        void IUIInternal.SetInteractable(bool value) => SetInteractable(value);
        private void SetInteractable(bool value) {
            CanvasGroup.interactable = value;
        }

        void IUIInternal.SetBlocksRaycasts(bool value) => SetBlocksRaycasts(value);
        private void SetBlocksRaycasts(bool value) {
            CanvasGroup.blocksRaycasts = value;
        }
    }
}

namespace Internal.KuiHuaBaoDian.Services.UI {

    public interface IUIInternal {

        void Initialize(Canvas canvas, IUIEventDispatcher eventDispatcher);

        int SetSortingOrder(int value);

        void SetSortingLayer(int value);

        void SetSortingLayerName(string value);

        void SetActive(bool value);

        void SetInteractable(bool value);

        void SetBlocksRaycasts(bool value);

        UniTask Instantiated();

        UniTask OnBackIn();

        UniTask OnGotoOut();

        GameObject GameObject { get; }
    }
}